package com.neuedu.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.neuedu.core.NeueduException;
import com.neuedu.entity.UmsStudent;
import com.neuedu.mapper.UmsStudentMapper;
import com.neuedu.service.UmsStudentService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.neuedu.util.JwtUtil;
import com.neuedu.util.SystemUser;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.errors.*;
import jakarta.annotation.Resource;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import jakarta.mail.internet.MimeMessage;

/**
 * <p>
 * 学生表 服务实现类
 * </p>
 *
 * @author wangyu
 * @since 2025-09-03
 */
@Service
public class UmsStudentServiceImpl extends ServiceImpl<UmsStudentMapper, UmsStudent> implements UmsStudentService {

    @Resource
    PasswordEncoder passwordEncoder;
    @Resource
    MinioClient minioClient;
    @Resource(name = "hjredistemplate")
    RedisTemplate<String, Object> redisTemplate;
     @Resource
     private MailServiceImpl mailService;



    @Override
    public boolean add(UmsStudent umsStudent) {
        umsStudent.setPassword(passwordEncoder.encode("123456"));
        return this.save(umsStudent);
    }

    @Override
    public boolean check(String field, String value, Long id) {
        QueryWrapper<UmsStudent> wrapper = new QueryWrapper<>();
        wrapper.eq(field, value);
        if (null != id) {
            wrapper.ne("id", id);
        }
        return this.count(wrapper) == 0;
    }

    @Override
    public boolean delete(UmsStudent umsStudent) {
        return this.updateById(umsStudent);
    }

    @Override
    public String login(String username, String password) {
        QueryWrapper<UmsStudent> wrapper = new QueryWrapper<>();
        wrapper.eq("phone", username)
                .or().eq("email", username);
        UmsStudent student = this.getOne(wrapper);
        if (null == student || !passwordEncoder.matches(password, student.getPassword())) {
            throw new NeueduException("用户名或密码错误");
        }
        if (!student.getActive()) {
            throw new NeueduException("该学生已经禁用，无法登录");
        }
        redisTemplate.opsForValue().set("student::" + student.getId(), student, 90, TimeUnit.MINUTES);
        return JwtUtil.create(new SystemUser(student.getId(), student.getIcon(), "student"));
    }

    @Override
    public boolean uploadIcon(MultipartFile file, Long id) throws IOException, ServerException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        String bucket = "images";
        StringBuilder builder = new StringBuilder();
        builder.append(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS")));
        Random random = new Random(id);
        for (int i = 0; i < 5; i++) {
            builder.append(random.nextInt(0,10));
        }
        builder.append(".")
                .append(FilenameUtils.getExtension(file.getOriginalFilename()));
        PutObjectArgs args = PutObjectArgs.builder()
                        .contentType(file.getContentType())
                        .bucket(bucket)
                        .object(builder.toString())
                        .stream(file.getInputStream(), file.getSize(), 0)
                        .build();
        minioClient.putObject(args);
        UmsStudent student = new UmsStudent();
        student.setId(id);
        student.setIcon(String.format("%s/%s", bucket, builder.toString()));
        return this.updateById(student);
    }

    @Override
    @Cacheable(value = "student", key = "#id")
    public UmsStudent getStudent(Long id) {
        return this.getById(id);
    }

    @Override
    public Boolean sendVerificationCode(String email) {
        String code = UUID.randomUUID().toString().substring(0, 4);
        String subject = "邮箱验证码";
        String text = "<h2>尊敬的用户<h2>" +
                "<p>您正在使用邮箱收取验证码</p>" +
                "<p>您的验证码是:" + code + "</p>";
        String existingCode = (String) redisTemplate.opsForValue().get(email);
        if (existingCode != null) {
            throw new NeueduException("验证码已发送,请稍后再试");
        }

        mailService.send(email, subject, text);
       // redisTemplate.opsForValue().set(email, code);
        // 关键修复：存入Redis时设置过期时间（2分钟 = 120秒）
        // 参数说明：key=email, value=code, timeout=120, unit=TimeUnit.SECONDS（秒）
        redisTemplate.opsForValue().set(email, code, 5, TimeUnit.MINUTES);
        return true;
    }

    @Override
    public Boolean verifyCode(String email, String code) {
        String storedCode = (String) redisTemplate.opsForValue().get(email);
        if(code.equals(storedCode)){
            redisTemplate.delete(email);
            return true;
        }
        throw new NeueduException("验证码错误,请重新输入");
    }

    @Override
    public Boolean resetPassword(String email, String password) {
        QueryWrapper<UmsStudent> wrapper = new QueryWrapper<>();
        wrapper.eq("email", email);
        String encodedPassword = passwordEncoder.encode(password);
        UmsStudent student = this.getOne(wrapper);
        if(null == student) {
            throw new NeueduException("用户不存在");
        }
        if (passwordEncoder.matches(password, student.getPassword())) {
            throw new NeueduException("新密码不能与旧密码相同");
        }
        student.setPassword(encodedPassword);
        return this.updateById(student);
    }
    @Resource
    private UmsStudentMapper umsStudentMapper;
    @Override
    public boolean existsById(Long userId) {
        if (userId == null) {
            return false;
        }

        // 使用 MyBatis-Plus 自带的 exists
        QueryWrapper<UmsStudent> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", userId); // 匹配用户表的主键（假设用户表主键是 id，对应 userId）
        return umsStudentMapper.exists(queryWrapper);
    }

    @Override
    public List<UmsStudent> uploadList(MultipartFile file) throws IOException {
        return EasyExcel.read(file.getInputStream(), UmsStudent.class, new AnalysisEventListener<UmsStudent>() {
            @Override
            public void invoke(UmsStudent o, AnalysisContext analysisContext) {
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext analysisContext) {
            }
        }).sheet(0).doReadSync();
    }

    @Override
    @Transactional
    public boolean saveBatch(String list) {
        List<UmsStudent> students = JSON.parseArray(list, UmsStudent.class);
        for (UmsStudent student : students) {
            student.setPassword(passwordEncoder.encode(student.getPhone()));
        }
        return this.saveBatch(students);
    }

    @Override
    public List<UmsStudent> listByName(String name) {
        QueryWrapper<UmsStudent> queryWrapper = new QueryWrapper<>();
        if (StringUtils.isNoneBlank(name)) {
            queryWrapper.like("name", name);
        }
        List<UmsStudent> list = this.list(queryWrapper);
        if (list.isEmpty()) {
            throw new NeueduException("无此用户");
        }
        return list;
    }
}


